#pragma once

#include "Common.hpp"

namespace SocketModule
{
    using namespace LogModule;

    const int gbacklog = 16; // 缺省的监听队列长度

    // 套接字基类
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void SocketOrDie() = 0;                                // 创建套接字，失败则退出程序
        virtual void BindOrDie(uint16_t port) = 0;                     // 绑定地址，失败则退出程序
        virtual void ListenOrDie(int backlog) = 0;                     // 监听套接字，失败则退出程序
        virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;  // 接受连接，失败则退出程序
        virtual void Close() = 0;                                      // 关闭套接字
        virtual int Recv(std::string *out) = 0;                        // 接收数据
        virtual int Send(const std::string &msg) = 0;                  // 发送数据
        virtual int Connect(const std::string &ip, uint16_t port) = 0; // 连接服务器，失败则退出程序

    public:
        // TCP server创建方法
        void BindTcpSocketMethod(uint16_t port, int backlog = gbacklog)
        {
            SocketOrDie();        // 创建套接字
            BindOrDie(port);      // 绑定地址
            ListenOrDie(backlog); // 监听套接字
        }

        // TCP client创建方法
        void BuildTcpClientSocketMethod()
        {
            SocketOrDie(); // 创建套接字
        }

        /* // UDP创建方法
        void BindUdpSocketMethod()
        {
            SocketOrDie(); // 创建套接字
            BindOrDie();   // 绑定地址
        } */
    };

    const int defaultsockfd = -1; // 默认套接字描述符
    // TCP套接字派生类
    class TcpSocket : public Socket
    {
    public:
        // 构造函数1, 无参构造
        TcpSocket() : _sockfd(defaultsockfd)
        {
        }

        // 构造函数2, 传入套接字描述符有参构造
        TcpSocket(int sockfd) : _sockfd(sockfd) {}

        // 创建套接字函数
        void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
            if (_sockfd < 0)
            {
                // 创建失败
                LOG(LogLevel::FATAL) << "socket error, 创建TCP套接字失败, 已退出!";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::DEBUG) << "socket success, 创建TCP套接字成功!"; // 创建成功
        }

        // 绑定地址函数
        void BindOrDie(uint16_t port) override
        {
            InetAddr addr(port);
            int n = ::bind(_sockfd, addr.NetAddrPtr(), addr.NetAddrLen()); // 绑定地址
            if (n < 0)
            {
                // 绑定失败
                LOG(LogLevel::FATAL) << "bind error, 绑定地址失败, 已退出!";
                exit(BIND_ERR);
            }
            LOG(LogLevel::DEBUG) << "bind success, 绑定地址成功!"; // 绑定成功
        }

        void ListenOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog); // 监听套接字
            if (n < 0)
            {
                // 监听失败
                LOG(LogLevel::FATAL) << "listen error, 监听套接字失败, 已退出!";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::DEBUG) << "listen success, 监听套接字成功!"; // 绑定成功
        }

        // 接收连接函数
        std::shared_ptr<Socket> Accept(InetAddr *client) override
        {
            struct sockaddr_in peer;      // 客户端地址
            socklen_t len = sizeof(peer); // 获取客户端地址长度

            int fd = ::accept(_sockfd, CONV(peer), &len); // 接收
            if (fd < 0)
            {
                // 接收失败
                LOG(LogLevel::WARNING) << "accept error, 接收失败!";
                return nullptr;
            }

            client->SetAddr(peer); // 设置保存客户端地址
            return std::make_shared<TcpSocket>(fd);
        }

        // 接收数据函数
        int Recv(std::string *out) override
        {
            char buffer[4096*4];
            ssize_t n = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0); // 接收数据
            if (n > 0)
            {
                buffer[n] = '\0';
                *out += buffer;
            }

            return n;
        }

        // 发送数据函数
        int Send(const std::string &msg) override
        {
            return ::send(_sockfd, msg.c_str(), msg.size(), 0); // 发送数据
        }

        // 连接服务器函数
        int Connect(const std::string &ip, uint16_t port) override
        {
            InetAddr server(ip, port); // 获取服务器地址
            int ret = ::connect(_sockfd, server.NetAddrPtr(), server.NetAddrLen());
            return ret;
        }

        // 关闭文件描述符
        void Close() override
        {
            if (_sockfd > 0)
            {
                ::close(_sockfd);
            }
        }

    private:
        int _sockfd; // 套接字描述符
    };

    /* // UDP套接字派生类
    class UdpSocket : public Socket
    {
    public:
        ~UdpSocket() {}

    }; */

}